package com.toddsoft.toddtris.model;

/**
 * A block represents one of the pieces which fall into the Toddtris {@link well}.
 * 
 * Seven different blocks numbered 0-6 are possible and these can each have four
 * orientations defined by the rotation index of 0-3.
 * 
 * Blocks can be rotated clockwise or anticlockwise. Blocks may also be reset
 * to the default rotation index of 0 to allow the blocks to be re-used.
 * 
 * The Block class has been written to pre-calculate all 28 block and orientation
 * combinations to avoid object creation and reduce cpu usage, avoiding slow down
 * on small devices.
 * 
 * @author todda
 *
 */
public class Block {
	
	/** The number of different blocks */ 
    public static final int NUM_BLOCKS = 7;
    
    /*
     * An array of square matrices which define the seven blocks in the default
     * orientation (rotation index = 0).
     */
    private static final int [][][] BASE_ELEMS = 
            {
                {
                    {1, 1},
                    {1, 1}
                },
                {
                    {0, 2, 0},
                    {0, 2, 2},
                    {0, 2, 0}
                },
                {
                    {0, 3, 3},
                    {0, 3, 0},
                    {0, 3, 0}
                },
                {
                    {0, 4, 0},
                    {0, 4, 0},
                    {0, 4, 4}
                },
                {
                    {0, 5, 0, 0},
                    {0, 5, 0, 0},
                    {0, 5, 0, 0},
                    {0, 5, 0, 0}
                },
                {
                    {0, 0, 6},
                    {0, 6, 6},
                    {0, 6, 0}
                },
                {
                    {0, 7, 0},
                    {0, 7, 7},
                    {0, 0, 7}
                }
            };
    
    /*
     * The baseElems array is used to calculate the allElems array.
     * The allElems array contains the seven blocks in each of their
     * four orientations.
     * 
     * first index is block number, second is rotation index,
     * third and forth are the shape of the block
     */
    private static final int ALL_ELEMS[][][][] 
            = new int [BASE_ELEMS.length][4][][];

    static {
        //initialise allBlocks
        for (int i = 0; i < BASE_ELEMS.length; i++) {
            int[][] currentElems = BASE_ELEMS[i];
            for (int j = 0; j < 4; j++) {
                ALL_ELEMS[i][j] = currentElems;
                if (j < 3) {
                    currentElems = rotateMatrix(currentElems);
                }
            }
        }     
    }
        
	/* 
	 * The block number and rotation index are all the state required.
	 * The actual structure of the block is looked up in the static
	 * array allElems. 
	 */
    private int blockNum;
    private int rotationIndex;

    /**
     * Create a new Block with the given index
     * 
     * @param blockNum should be between 0 and {@link Block#NUM_BLOCKS} - 1 
     * 		inclusive 
     */
    Block(int blockNum) {
        this(blockNum, 0);     
    }
    
    /*
     * @param blockNum
     * @param rotationIndex
     */
    private Block(int blockNum, int rotationIndex) {
        this.blockNum = blockNum;
        this.rotationIndex = rotationIndex;     
    }
    
    /*
     * Method used to initialise the allElems array.
     * @return a matrix corresponding to the matrix m rotated clockwise.
     */
    private static int[][] rotateMatrix(int[][] m) {
        int [][] rotated = new int[m.length][m.length];
        for (int i = 0; i < m.length; i++) {
            for (int j = 0; j < m.length; j++) {
                rotated[m.length - 1 - j][i] = m[i][j];
            }
        }
        return rotated;     
    }
    
    /**
     * Get the size of the block which corresponds to the first dimension of 
     * the square matrix representing this block.
     * 
     * @return int
     */
    public int getSize() {
        return ALL_ELEMS[blockNum][rotationIndex].length;     
    }
    
    /**
     * @param x
     * @param y
     * @return int
     */
    public int getElem(int x, int y) {
        return ALL_ELEMS[blockNum][rotationIndex][x][y];     
    }
    
    void rotate() {
        rotationIndex = (++rotationIndex) % 4;     
    }
    
    void antirotate() {
        rotationIndex = (rotationIndex + 3) % 4;     
    }
    
    void reset() {
        rotationIndex = 0;     
    }
    
    /**
     * @return int
     */
    int getBlockNum() {
        return blockNum;     
    }
    
    /**
     * @return int
     */
    int getRotationIndex() {
        return rotationIndex;     
    }
}
